// pkg/registry/client.go
package registry

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
)

// ImageRef OCI 镜像引用
type ImageRef struct {
	Registry string // e.g., "docker.io"
	Repo     string // e.g., "library/alpine"
	Tag      string // e.g., "latest"
	Digest   string // 可选：sha256:xxxx
}

// LayerDescriptor 层描述符
type LayerDescriptor struct {
	Digest    string `json:"digest"`
	Size      int64  `json:"size"`
	MediaType string `json:"mediaType"`
}

// ManifestConfig 镜像清单配置
type ManifestConfig struct {
	Architecture string `json:"architecture"`
	Config       string `json:"config"`
	Digest       string `json:"digest"`
	MediaType    string `json:"mediaType"`
	Size         int64  `json:"size"`
}

// Manifest OCI 镜像清单
type Manifest struct {
	SchemaVersion int               `json:"schemaVersion"`
	MediaType     string            `json:"mediaType"`
	Config        ManifestConfig    `json:"config"`
	Layers        []LayerDescriptor `json:"layers"`
}

// Client OCI Registry 客户端
type Client struct {
	httpClient *http.Client
	authToken  string
}

// NewClient 创建 Registry 客户端
func NewClient() *Client {
	return &Client{
		httpClient: &http.Client{
			Timeout: 300, // 5 分钟
		},
	}
}

// PullManifest 拉取镜像清单
func (c *Client) PullManifest(ctx context.Context, ref *ImageRef) (*Manifest, error) {
	// 构建 API URL
	url := fmt.Sprintf("https://%s/v2/%s/manifests/%s",
		ref.Registry, ref.Repo, ref.Tag)
	if ref.Digest != "" {
		url = fmt.Sprintf("https://%s/v2/%s/manifests/%s",
			ref.Registry, ref.Repo, ref.Digest)
	}

	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, err
	}

	// 设置 Accept 头支持多种 manifest 类型
	req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
	req.Header.Set("Accept", "application/vnd.oci.image.manifest.v1+json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("http request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("http status %d", resp.StatusCode)
	}

	var manifest Manifest
	if err := json.NewDecoder(resp.Body).Decode(&manifest); err != nil {
		return nil, fmt.Errorf("decode manifest failed: %w", err)
	}

	return &manifest, nil
}

// PullLayer 拉取单个层
func (c *Client) PullLayer(ctx context.Context, ref *ImageRef, digest string) (io.ReadCloser, error) {
	// 构建 blob API URL
	url := fmt.Sprintf("https://%s/v2/%s/blobs/%s",
		ref.Registry, ref.Repo, digest)

	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("http request failed: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("http status %d", resp.StatusCode)
	}

	return resp.Body, nil
}

// PullLayerPartial 按范围拉取层
func (c *Client) PullLayerPartial(ctx context.Context, ref *ImageRef, digest string, offset, length int64) ([]byte, error) {
	// 构建 blob API URL
	url := fmt.Sprintf("https://%s/v2/%s/blobs/%s",
		ref.Registry, ref.Repo, digest)

	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, err
	}

	// 设置 Range 头
	req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+length-1))

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("http request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusPartialContent && resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("http status %d", resp.StatusCode)
	}

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response failed: %w", err)
	}

	return data, nil
}

// ParseImageRef 解析镜像引用
func ParseImageRef(refStr string) (*ImageRef, error) {
	// 格式: registry/repo:tag 或 registry/repo@sha256:digest
	parts := strings.Split(refStr, "/")
	if len(parts) < 2 {
		return nil, fmt.Errorf("invalid image reference: %s", refStr)
	}

	registry := parts[0]
	rest := strings.Join(parts[1:], "/")

	ref := &ImageRef{Registry: registry}

	// 检查 tag 和 digest
	if strings.Contains(rest, "@") {
		// 格式: repo@digest
		tagParts := strings.Split(rest, "@")
		ref.Repo = tagParts[0]
		ref.Digest = tagParts[1]
	} else if strings.Contains(rest, ":") {
		// 格式: repo:tag
		tagParts := strings.Split(rest, ":")
		ref.Repo = tagParts[0]
		ref.Tag = tagParts[1]
	} else {
		// 仅 repo，使用默认 tag
		ref.Repo = rest
		ref.Tag = "latest"
	}

	return ref, nil
}

// ---
